package com.zhanglijie.base.sync.Lock;

import java.lang.reflect.Field;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;


import lombok.Data;
import sun.misc.Unsafe;

/**
 * @author zhanglijie
 * @version 1.0
 * @since 1.1.0 2022/6/8 0008 22:25
 */
@Data
public class ObjectMonitor {
    //线程重入次数
    private int recursions=0;

    //表示拥有monitor的线程
    private volatile Thread owner = null;
    //多线程竞争锁进入时的单向链表(真实是栈结构这里用链表省略操作）
    private LinkedBlockingQueue cxq = new LinkedBlockingQueue();
    //线程执行wait方法后进入的双向队列
    private LinkedBlockingDeque waitSet= null;

    public Thread getOwner() {
        return owner;
    }

    public void setOwner(Thread owner) {
        this.owner = owner;
    }

    //
    private LinkedBlockingQueue entryList = null;

    public Thread comAndChgOwner(MyLock myLock){
        Unsafe unsafe = MyUnsafe.getUnsafe();
        ObjectMonitor objectMonitor = myLock.getMarkWord().getPtrMonitor();
        Field owner =  null;
        try{
            owner =  objectMonitor.getClass().getDeclaredField("owner");
        }catch (Exception e){

        }
        long offset = unsafe.objectFieldOffset(owner);
        Thread currentThread = Thread.currentThread();
        boolean flag = unsafe.compareAndSwapObject(objectMonitor, offset, null, currentThread);
        if(flag){
            return null;
        }
        return objectMonitor.getOwner();
    }
    public void entery(MyLock myLock){
       //1 cas修改owner 成功就是获得锁 否则就没获得锁
        Thread thread = comAndChgOwner(myLock);
        if(thread == null){//第一次获得锁 重入次数是0
            return;
        }
        //2.如果之前的owner指向当前线程就表示获得锁了 重入次数+1
        if(thread == Thread.currentThread()){
            recursions++;
            return;
        }
       //3.从轻量级锁膨胀而来（这个表明轻量级时候已经获得轻量级锁，而此时已经上升到重量级锁 所以Lock的所有级别锁的owner都应该指向当前线程，因此重入直接给1）
        LockRecord lockRecord = MySynchronized.threadLocal.get();
        MarkWord slowWord = lockRecord.getMarkWord();
        if(slowWord != null){
            recursions = 1;
            owner = Thread.currentThread();
            return;
        }

        //4.再自旋十次看看
        for (int i = 0; i < 10; i++) {
            if(tryLock(myLock)>0){
                return;//抢占成功return
            }
        }
       //4.预备入队挂起
       EnterI(myLock);
    }

    private void EnterI(MyLock myLock) {
        //自旋
        if(tryLock(myLock)>0)  return;//抢占成功return
        //延迟下
        try { Thread.sleep(10); }catch (Exception e){}
        //再自旋十次
        for (int i = 0; i < 10; i++) {
            if(tryLock(myLock)>0)return;//抢占成功return
        }
        //到此自旋全部失败 下面入队
        ObjectWaiter objectWaiter = new ObjectWaiter(Thread.currentThread());
        for(;;){
            try{
                cxq.put(objectWaiter);
                break;
            }catch (Exception e){
                if(tryLock(myLock)>0){
                    return;//抢占成功return
                }
            }
        }
      // 没成功就要阻塞（前提已经入队了）
        for(;;){
            if(tryLock(myLock)>0)  return;//抢占成功return
            Unsafe unsafe = MyUnsafe.getUnsafe();
            unsafe.park(false,0L);
            if(tryLock(myLock)>0)  return;//唤醒后 再尝试抢锁
            //延迟下
            try { Thread.sleep(10); }catch (Exception e){}
        }
    }

    private int tryLock(MyLock myLock) {
        for(;;){
            //如果有线程已经占据了锁 直接return 0,后面就不自旋
             if(owner != null){
                 return 0;
             }
            Thread thread = comAndChgOwner(myLock);
             //成功
            if(thread == null){
                return 1;
            }else{
                return -1;//抢占失败
            }
        }
    }

    /**
     * 重量级锁退出
     * @param myLock
     */
    public void exit(MyLock myLock) {
        Thread currentThread = Thread.currentThread();
        if(owner != currentThread){
            LockRecord lockRecord = MySynchronized.threadLocal.get();
            MarkWord head = lockRecord.getMarkWord();
            if(head != null){//这是从轻量级锁过来的
                owner = currentThread;
                recursions = 0;
            }else{
                throw new RuntimeException("cur thread not sync lock owner,have no right to release!!");
            }
        }
        //重入锁的释放过程
        if(recursions != 0 ){
            recursions--;
            return;
        }
        /**
         * 唤醒模式一共有三种模式 这里只演示最简单的模式2，直接唤醒cxq的模式
         */
        //触发内存屏障
        Unsafe unsafe = MyUnsafe.getUnsafe();
        //从队列里面获取个线程 准备唤醒它
        ObjectWaiter objectWaiter = (ObjectWaiter)cxq.poll();
        if(objectWaiter != null){
            exitEpilog(myLock,objectWaiter);
        }
    }

    /**
     * 唤醒线程
     * @param myLock
     */
    private void exitEpilog(MyLock myLock,ObjectWaiter objectWaiter ) {
        //丢弃锁 owner设置为null
        myLock.getMarkWord().getPtrLockRecord().setOwner(null);
        //获取线程唤醒
        Thread sleepThread = objectWaiter.getWait();
        Unsafe unsafe = Unsafe.getUnsafe();
        unsafe.unpark(sleepThread);
        objectWaiter = null;
        myLock.getMarkWord().setPtrLockRecord(null);
    }
}
